#include <errno.h>
#include <linux/videodev2.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <thread>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "UsbHotPlug.h"
#include "UsbAVStream.h"
#include "log.h"


namespace Media{
#define V4L2_BUF_TYPE (V4L2_BUF_TYPE_VIDEO_CAPTURE)
#define UVCNAMECMD "/sys/class/video4linux/video%d/name"
#define NEARHUB "NearHub"
#define TAIL    "Tail"

static mutex UsbHotPlug::m_infoLock;

void UsbHotPlug::instance(){
	//开启两条线程分别调用uac音频输入输出的热插拔
	thread uacInHotPlug(UacInHotPlug);
	uacInHotPlug.detach();
	thread uacOutHotPlug(UacOutHotPlug);
	uacOutHotPlug.detach();
}


void  UsbHotPlug::UacInHotPlug(void){
	set<string> oldset;
	set<string> newset;
	set<string>::iterator it;
	map<string, int> map;
	std::map<string, int>::iterator mapit;

	//第一次oldset的内容从UacIn中读取
	UsbAVStream::instance() -> getAblilityMap(map);
	for(mapit = map.begin(); mapit != map.end(); mapit++){
		if(((mapit -> second) & UACIN) == 1){
			oldset.insert(mapit -> first);
		}
	}
	while(1){
		//查看当前文件中所有的uac输入设备以及设备节点信息
		//对比两个标识符看当前map中的设备是否是最新的
		if(getDeviceSet(newset, true) && !compareSet(newset, oldset)){
			set<string> temp;
			//遍历新的设备节点创建出新的设备
			for(it = newset.begin(); it != newset.end(); it++){
				if(!oldset.count((*it))){
					string usbPort = (*it);
					int soundnum;
					if(getSoundNum(usbPort, soundnum)){
						//创建
						UsbAVStream::instance() -> addDevice(usbPort, UACIN, soundnum);
					}
				}
			}
			//遍历旧的设备set删除多余的设备
			for(it = oldset.begin(); it != oldset.end(); it++){
				if(!newset.count(*it)){
					string usbPort = (*it);
					UsbAVStream::instance() -> removeDevice(usbPort, UACIN);
				}
			}
			//交换两个set的对象
			oldset.clear();
			temp = oldset;
			oldset = newset;
			newset = oldset;
			for(it = oldset.begin(); it != oldset.end(); it++){
				LOG_PrintInfo("UacIn set reflashd member %s\n", it -> c_str());
			}
		}else{
			//没有发生变化休眠
			std::this_thread::sleep_for(std::chrono::milliseconds(2 * 100));
		}
	}
}

void UsbHotPlug::UacOutHotPlug(void){
	set<string> oldset;
	set<string> newset;
	set<string>::iterator it;
	map<string, int> map;
	std::map<string, int>::iterator mapit;

	//第一次oldset的内容从UacIn中读取
	UsbAVStream::instance()->getAblilityMap(map);
	for(mapit = map.begin(); mapit != map.end(); mapit++){
		if(((mapit->second) & UACOUT) == 1){
			oldset.insert(mapit->first);
		}
	}
	while(1){
		//查看当前文件中所有的uac输入设备以及设备节点信息
		//对比两个标识符看当前map中的设备是否是最新的
		if(getDeviceSet(newset, false) && !compareSet(newset, oldset)){
			set<string> temp;
			//遍历新的设备节点创建出新的设备
			for(it = newset.begin(); it != newset.end(); it++){
				if(!oldset.count((*it))){
					string usbPort = (*it);
					int soundnum;
					if(getSoundNum(usbPort, soundnum)){
						//创建
						UsbAVStream::instance()->addDevice(usbPort, UACOUT, soundnum);
					}
				}
			}
			//遍历旧的设备set删除多余的设备
			for(it = oldset.begin(); it != oldset.end(); it++){
				if(!newset.count(*it)){
					string usbPort = (*it);
					UsbAVStream::instance()->removeDevice(usbPort, UACOUT);
				}
			}
			//交换两个set的对象
			oldset.clear();
			temp = oldset;
			oldset = newset;
			newset = oldset;
			for(it = oldset.begin(); it != oldset.end(); it++){
				LOG_PrintInfo("UacIn set reflashd member %s\n", it->c_str());
			}
		}else{
			//没有发生变化休眠
			std::this_thread::sleep_for(std::chrono::milliseconds(2 * 100));
		}
	}
}


bool UsbHotPlug::compareSet(set<string> setTemp1, set<string> setTemp){
	if (setTemp.size() != setTemp1.size()){
		//LOG_PrintWarn("compare table start %d\n", false);
		return false;
	}

	set<string>::iterator it;
	set<string>::iterator it1;
	//遍历集合中的所有元素
	bool flag = true;
	for (it = setTemp.begin(), it1 = setTemp1.begin(); it != setTemp.end(); it++, it1++){
		if (*it1 != *it){
			flag = false;
			break;
		}
	}
	//LOG_PrintInfo("compare table start %d\n", flag);
	return flag;
}

bool UsbHotPlug::getDeviceSet(set<string> &curset, bool isCapture){
	lock_guard<mutex> lock(m_infoLock);
	set<string> dev;
	set<string> cards;
	set<string>::iterator it;
	char captureNode[16] = {0};
	char playNode[16] = {0};
	//清空set
	curset.clear();
	//获取当前声卡和设备节点的信息
	if(!UacUtil::getSoundInfo(dev, cards)){
		return false;
	}
	//构建出最新的设备节点的set
	for(it = cards.begin(); it != cards.end(); it++){
		bool isTail = (it -> find(TAIL) != it -> npos) && (it -> find(NEARHUB) != it -> npos);
		if(it -> find(uacSoundMark) == it -> npos || isTail ){
			//LOG_PrintWarn("it -> find(uacSoundMark) == it -> npos:%d   isTail:%d\n", it -> find(uacSoundMark) == it -> npos, isTail);
			continue;
		}
		int soundnum = (*it)[1] - 48;
		//截取出usb口的描述信息
		int start = it -> find(usbportMark);
		int end = it -> find(",", start);
		string usbport = it -> substr(start + 1, end - start - 1);
		if(usbport.find("usb-1.1") != string::npos){
			usbport = "usb-1.1";
		}else if(usbport.find("usb-1.2") != string::npos){
			usbport = "usb-1.2";
		}else{
			continue;
		}
		sprintf(captureNode, captureDeviceFormat, soundnum);
		sprintf(playNode, playDeviceFormat, soundnum);
		if(isCapture && dev.count(captureNode)){
			string key = usbport;
			curset.insert(key);
		}
		if(!isCapture && dev.count(playNode)){
			string key = usbport;
			curset.insert(key);
		}
	}
#if 0
	for(it = curset.begin(); it != curset.end(); it++){
		LOG_PrintInfo("mode:%d curset member:%s\n", isCapture, it -> c_str());
	}
#endif
	return true;
}

bool UsbHotPlug::getSoundNum(string usbport, int &soundnum){
	set<string> dev;
	set<string> cards;
	set<string>::iterator cardit;
	if(!UacUtil::getSoundInfo(dev, cards)){
		LOG_PrintError("faild to getd device_node info and soundcards info\n");
		return false;
	}

	soundnum = -1;
	for(cardit = cards.begin(); cardit != cards.end(); cardit++){
		if((*cardit).find(usbport) == (*cardit).npos){
			continue;
		}
		soundnum = (*cardit)[1] - 48;
		break;
	}

	return true;
}


int UsbHotPlug::checkUvcVideoId(set<int> &uAddSet)
{
    FILE *fp = NULL;
    char buf[1024];
    int i;
    char cmd[128];
    
    for (i = 0; i < 100; i++)
    {
        snprintf(cmd, sizeof(cmd), UVCNAMECMD, i);
        if (access(cmd, F_OK))
            continue;
        fp = fopen(cmd, "r");
        if (fp)
        {
            if (fgets(buf, sizeof(buf), fp))
            {
                if(strstr(buf, "usb") || strstr(buf, "gadget") || (strstr(buf, "NearHub") && strstr(buf, "Tail")))
                {
                    uAddSet.insert(i);
                    //LOG_PrintInfo("Found uvc video port:%d.\n", i);
                }
            }
            fclose(fp);
        }
    }

    return 0;
}

bool UsbHotPlug::isTail(int number){
	FILE *fp = NULL;
	char cmd[128];
	char buf[1024];
	snprintf(cmd, sizeof(cmd), UVCNAMECMD, number);
    if (access(cmd, F_OK))
        return false;
    fp = fopen(cmd, "r");
    if (fp)
    {
        if (fgets(buf, sizeof(buf), fp))
        {
            if(strstr(buf, "NearHub") && strstr(buf, "Tail"))
            {
             	fclose(fp);
				return true;
            }
        }
        fclose(fp);
    }
	return false;
}

void UsbHotPlug::searchVideo()
{
	char devpath[64] = {0};
	struct v4l2_capability cap;
	const char *driver = "uvcvideo";
	stringstream ss;
	//遍历dev下的所有文件
	set<int> uAdd;
	int usbout_number =  checkUvcVideoId(uAdd);
	for(int i = 0; i < 100; i++){
		if(uAdd.find(i) != uAdd.end()){
			//LOG_PrintWarn("uAdd.find(i) != uAdd.end(): %d\n", i);
			continue;
		}
		//尝试打开文件失败直接下一个
		sprintf(devpath, FULLDEVPATH.c_str(), i);
		if (access(devpath, F_OK))
            continue;
		int fd = open(devpath, O_RDWR);
		if(fd < 0){
			continue;
		}else{
			//可以打开成功就接着判断设备是否是一个视频源
			do{
				/* check video decive driver capability */
				if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
					LOG_PrintError("The Current device is not a cannot open %d\n", i);
					break;
				}

				/*judge wherher or not to be a video-get device*/
				if (!(cap.capabilities & V4L2_BUF_TYPE)) {
					LOG_PrintError("The Current device is not a video capture device %d\n", i);
					break;
				}
				/*judge whether or not to supply the form of video stream*/
				if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
					LOG_PrintError("The Current device does not support streaming i/o %d\n", i);
					break;
				}
				//判断当前设备的驱动是不是uvc
				if(!(strcmp((const char *)cap.driver, driver) == 0)){
					LOG_PrintError("The Current device does not a uvcvideo i/o %d     %s\n", i, strerror(errno));
					break;
				}
				string usbport;
				string node = NODEPREFIX + to_string(i);
				getUsbName(node, usbport);
				LOG_PrintInfo("search node:%s\n", node.c_str());
				UsbAVStream::instance() -> addDevice(usbport, UVCIN, i);
				break;
			}while(1);
			close(fd);
		}
	}
	return ;
}




std::vector<std::string> UsbHotPlug::stringSplit(const std::string& str, char delim) {
    std::stringstream ss(str);
    std::string item;
    std::vector<std::string> elems;
    while (std::getline(ss, item, delim)) {
        if (!item.empty()) {
            elems.push_back(item);
        }
    }
    return elems;
}




bool UsbHotPlug::getUsbName(string device_node_name, string &usbname){
	//拼接命令
	char cmd[128] = {0};
	FILE *fp = NULL;
	char buf[1024] = {0};
	char usb_number[64] = {0};
	char *token;
	int i = 0;

	sprintf(cmd, "/sys/class/video4linux/%s", device_node_name.c_str());
	//查看文件是否存在
	if (access(cmd, F_OK))
	{
		LOG_PrintError("cannot find file: %s", cmd);
		return false;
	}
	//打开管道执行命令，获取
	sprintf(cmd, "readlink -f /sys/class/video4linux/%s", device_node_name.c_str());
	fp = popen(cmd, "r");
	if(fp)
	{
		if(fgets(buf, sizeof(buf), fp))
		{
			string devPath = buf;
			std::vector<std::string> result = stringSplit(devPath, '//');
			usbname = result[6];
			usbname = "usb-" + usbname.substr(2, usbname.length() - 2);
			pclose(fp);
			return true;
		}
	}
	return false;
}
}


